/**
 * netty服务端启动类
 */
package cn.com.klun.netty.server;

import cn.com.klun.business.TaskListenerServerService;
import cn.com.klun.netty.NettyMessage;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;

/**
 * @author chenmin-ds
 * netty服务端启动类
 */
public class NettyServerStartup {

	private Logger logger = LoggerFactory.getLogger("NettyServerStartup");
	private EventLoopGroup workGroup = null;
	private EventLoopGroup bossGroup = null;
	private ServerBootstrap bootstrap = null;
	private Channel channel = null;
	private ConcurrentMap<String, Channel> channelArray = new ConcurrentHashMap<String, Channel>();
	private int inetPort = 10440;
	private TaskListenerServerService taskListenerServerService;

	private static class Singleton {
		private static final NettyServerStartup singleton = new NettyServerStartup();
	}
	public static final NettyServerStartup getInstance() {
		return Singleton.singleton;
	}
	
	public void setTaskListenerServerService(TaskListenerServerService taskListenerServerService){
		this.taskListenerServerService = taskListenerServerService;
	}
	
	public void setRunPort(int inetPort){
		this.inetPort = inetPort;
	}
	public void stop(){
		if(channel != null){
			channel.close();
		}
		if(bossGroup != null){
			bossGroup.shutdownGracefully();
		}
		if(workGroup != null){
			workGroup.shutdownGracefully();
		}
		if(channelArray.size() > 0 ){
			channelArray.clear();
		}
	}

	public void start(){
		bootstrap = new ServerBootstrap();
		bossGroup = new NioEventLoopGroup();
		workGroup = new NioEventLoopGroup();
		bootstrap.group(bossGroup, workGroup);
		bootstrap.channel(NioServerSocketChannel.class);
		bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
		bootstrap.option(ChannelOption.TCP_NODELAY, true);
		bootstrap.option(ChannelOption.SO_BACKLOG, 1024);
		bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {

			@Override
			protected void initChannel(SocketChannel ch) throws Exception {
				ChannelPipeline channelPipeline = ch.pipeline();
				channelPipeline.addLast(new ObjectDecoder(1024*1024, ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())));
				channelPipeline.addLast(new ObjectEncoder());
				channelPipeline.addLast(new IdleStateHandler(170,180,190,TimeUnit.SECONDS));
				channelPipeline.addLast("nettyServerHeartBeatHandler",new NettyServerHeartBeatHandler(taskListenerServerService));
				channelPipeline.addLast("nettyServerTaskInfoHandler",new NettyServerTaskInfoHandler(taskListenerServerService));
			}
		});
		
		doBind();
		Runtime.getRuntime().addShutdownHook(new Thread(){
			@Override
            public void run() {
				workGroup.shutdownGracefully();
				bossGroup.shutdownGracefully();
			}
		});
	}
	
	private ChannelFuture doBind(){
		ChannelFuture channelFuture = null;
		try {
			channelFuture = bootstrap.bind(inetPort).sync();
		} catch (InterruptedException e) {
			logger.error("netty服务端绑定端口失败，inetPort=" + inetPort,e);
		}
		this.channel = channelFuture.channel();
		return channelFuture;
	}
	
	public void putChannel(String key , Channel value){
		channelArray.put(key, value);
	}
	
	
	public Channel getChannel(String key){
		return channelArray.get(key);
	}

	public boolean sendMsg(String ip, int port, NettyMessage message){
		final  NettyMessage _message = message;
		_message.setSendRequestOK(false);
		Channel channel = channelArray.get(ip +":" + port);
		try {
			if(null != channel && channel.isActive()){
				channel.writeAndFlush(message).sync().addListener(new ChannelFutureListener() {
					@Override
                    public void operationComplete(ChannelFuture future) throws Exception {
						if (future.isSuccess()) {
							_message.setSendRequestOK(true);
						}
					}
				});
			}else {
				logger.error( ip +":" + port + "channel is not exists" );
			}
		}catch (Exception e){
			logger.error("send msg to " + ip +":" + port + e );
		}
		return  _message.isSendRequestOK();
	}

	public void removeChannel(String key){
		try{
			if(channelArray.containsKey(key)){
				channelArray.get(key).close();
				channelArray.remove(key);
			}
		}catch(Exception e){
			logger.error("netty服务端关闭连接异常，key=" + key,e);
		}
	}
}
